package cn.xxluckey.util;



import java.nio.ByteBuffer;
import java.util.zip.CRC32;

public class PacketUtil {

    private static final byte START_FLAG = 0x68; // 起始标志
    private static final int MAX_DATA_LENGTH = 200; // 最大数据域长度

    /**
     * 封包方法
     *
     * @param sequenceNumber 序列号
     * @param encryptionFlag 加密标志
     * @param frameType 帧类型
     * @param payload 消息体
     * @return 封装好的数据包字节数组
     */
    public static byte[] pack(short sequenceNumber, byte encryptionFlag, byte frameType, byte[] payload) {
        if (payload.length > MAX_DATA_LENGTH) {
            throw new IllegalArgumentException("消息体长度不能超过 " + MAX_DATA_LENGTH + " 字节");
        }

        // 数据长度 = 序列号(2) + 加密标志(1) + 帧类型(1) + 消息体长度
        int dataLength = 2 + 1 + 1 + payload.length;

        ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + dataLength + 2); // 起始标志(1) + 数据长度(1) + 数据域 + 校验域(2)
        buffer.put(START_FLAG); // 起始标志
        buffer.put((byte) dataLength); // 数据长度
        buffer.putShort(sequenceNumber); // 序列号
        buffer.put(encryptionFlag); // 加密标志
        buffer.put(frameType); // 帧类型
        buffer.put(payload); // 消息体

        // 计算校验码
        short crc = calculateCRC(buffer.array(), 1, dataLength + 1); // 从序列号到消息体
        buffer.putShort(crc); // 校验码

        return buffer.array();
    }

    /**
     * 拆包方法
     *
     * @param packet 封装好的数据包字节数组
     * @return 解包后的数据对象
     */
    public static UnpackedData unpack(byte[] packet) {
        ByteBuffer buffer = ByteBuffer.wrap(packet);

        // 起始标志验证
        byte startFlag = buffer.get();
        if (startFlag != START_FLAG) {
            throw new IllegalArgumentException("起始标志不匹配");
        }

        // 数据长度验证
        byte dataLength = buffer.get();
        if (dataLength < 4 || dataLength > MAX_DATA_LENGTH + 4) { // 最小长度包含序列号、加密标志、帧类型
            throw new IllegalArgumentException("数据长度非法");
        }

        // 数据解析
        short sequenceNumber = buffer.getShort(); // 序列号
        byte encryptionFlag = buffer.get(); // 加密标志
        byte frameType = buffer.get(); // 帧类型
        byte[] payload = new byte[dataLength - 4];
        buffer.get(payload); // 消息体

        // 校验码验证
        short crc = buffer.getShort();
        short calculatedCRC = calculateCRC(packet, 1, dataLength + 1); // 从序列号到消息体
        if (crc != calculatedCRC) {
            throw new IllegalArgumentException("校验码验证失败");
        }

        // 返回解包后的数据
        return new UnpackedData(sequenceNumber, encryptionFlag, frameType, payload);
    }

    /**
     * CRC 校验计算
     *
     * @param data 数据字节数组
     * @param offset 起始位置
     * @param length 数据长度
     * @return CRC 校验码
     */
    private static short calculateCRC(byte[] data, int offset, int length) {
        CRC32 crc32 = new CRC32();
        crc32.update(data, offset, length);
        return (short) (crc32.getValue() & 0xFFFF);
    }

    /**
     * 解包后的数据对象
     */
    public static class UnpackedData {
        private final short sequenceNumber;
        private final byte encryptionFlag;
        private final byte frameType;
        private final byte[] payload;

        public UnpackedData(short sequenceNumber, byte encryptionFlag, byte frameType, byte[] payload) {
            this.sequenceNumber = sequenceNumber;
            this.encryptionFlag = encryptionFlag;
            this.frameType = frameType;
            this.payload = payload;
        }

        public short getSequenceNumber() {
            return sequenceNumber;
        }

        public byte getEncryptionFlag() {
            return encryptionFlag;
        }

        public byte getFrameType() {
            return frameType;
        }

        public byte[] getPayload() {
            return payload;
        }
    }
}
